package com.example.mall.common.core;

import com.example.mall.common.core.configuration.ExceptionCodeConfiguration;
import com.example.mall.common.core.exception.HttpException;
import com.example.mall.common.core.response.UnifyExceptionResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.List;

/**
 * 全局异常处理器
 */
@RestControllerAdvice
public class GlobalExceptionAdvice {
    @Autowired
    private ExceptionCodeConfiguration codeMsgConfiguration;

    /**
     * 处理未知异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public UnifyExceptionResponse handleException(HttpServletRequest req, Exception e) {
        // 日志 TODO
        e.printStackTrace();

        String message = codeMsgConfiguration.getMessage(9999);
        if (e instanceof MethodArgumentNotValidException) {
            System.out.println("参数检验的异常");
            System.out.println(e.getMessage());
            message = e.getMessage();
        }

        return new UnifyExceptionResponse(9999, message, getRequestStr(req));
    }

    /**
     * HttpException异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = HttpException.class)
    public ResponseEntity<UnifyExceptionResponse> handleHttpException(HttpServletRequest req, HttpException e) {
        // 日志 TODO
        e.printStackTrace();

        Integer code = e.getCode();
        String message = codeMsgConfiguration.getMessage(code);

        UnifyExceptionResponse unifyExceptionResponse = new UnifyExceptionResponse(code, message,
                getRequestStr(req));

        // 动态修改响应信息
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);

        HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());
        ResponseEntity<UnifyExceptionResponse> responseEntity = new ResponseEntity<>(
                unifyExceptionResponse, httpHeaders, httpStatus);
        return responseEntity;
    }

    /**
     * Bean校验器-异常处理
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public UnifyExceptionResponse handleBeanValidation(HttpServletRequest req, MethodArgumentNotValidException e) {
        /**
         * 校验器-异常处理
         *  - 收集错误消息列表
         *  - 日志 TODO
         */
        e.printStackTrace();

        String requestStr = getRequestStr(req);
        List<ObjectError> errors =  e.getBindingResult().getAllErrors();
        String message = getMethodArgumentNotValidMessage(errors);

        return new UnifyExceptionResponse(10000, message, requestStr);
    }

    /**
     * 请求路径参数校验-异常处理
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public UnifyExceptionResponse handleConstraintViolation(HttpServletRequest req, ConstraintViolationException e) {
        /**
         * 校验器-请求路径参数-错误
         *  - 收集错误消息列表
         *  - 日志 TODO
         */
        e.printStackTrace();
        String message = getConstraintViolationMessage(e);
        return new UnifyExceptionResponse(10000, message, getRequestStr(req));
    }

    /**
     * 从 MethodArgumentNotValidException - List<ObjectError> 中取出错误信息集 拼接字符串
     * @param errors
     * @return
     */
    private String getMethodArgumentNotValidMessage(List<ObjectError> errors) {
        StringBuffer messge = new StringBuffer();
        errors.forEach(error ->
                messge.append(error.getDefaultMessage()).append(';')
        );
        return messge.toString();
    }

    /**
     * ConstraintViolationException - 错误信息 拼接字符串
     * @param e
     * @return
     */
    private String getConstraintViolationMessage(ConstraintViolationException e) {
        StringBuffer messge = new StringBuffer();
        e.getConstraintViolations().forEach(error ->
                messge.append(error.getMessage()).append(';')
        );
        return messge.toString();
    }

    /**
     * 根据请求截取 请求方法和地址
     *  - exp: 'GET /admin/login'
     * @param req
     * @return
     */
    private String getRequestStr(HttpServletRequest req) {
        String method = req.getMethod();
        String url = req.getRequestURI();
        return method + " " + url;
    }
}
